﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Threading;
using Co9320.NamaAlert.Model;
using Co9320.NamaAlert.Model.TagServer;
using Co9320.NamaAlert.Shared;

namespace Co9320.NamaAlert.CommunicationControl
{
	internal sealed class ServerCommunicator:IDisposable
	{


		private static readonly TimeSpan DefaultErrorInterval=new TimeSpan(0, 20, 00);
		private static readonly TimeSpan DefaultOneWayInterval= new TimeSpan(0, 10, 00);


		private BackgroundWorker OneWayWorker;
		private BackgroundWorker InteractiveWorker;

		private DispatcherTimer OneWayTimer;
		private DispatcherTimer InteractiveTimer;

		private TagServerClient OneWayClient;
		private TagServerClient InteractiveClient;

		private OneWayTransactor OneWayTransactor;
		private InteractiveTransactor InteractiveTransactor;

		private bool IsOneWayNormalComplete;
		private bool IsInteractiveNormalComplete;

		private Co9320.NamaAlert.Model.TagServer.GetReturn QueryResult;

		private TokenProcessor TokenGenerator;

		public event EventHandler<TagsChangedEventArgs> TagChanged;


		public ServerCommunicator(string ApplicationId,string KeySeed)
			:this(ApplicationId,KeySeed,DefaultOneWayInterval,DefaultErrorInterval)
		{
		}

		public ServerCommunicator(string ApplicationId, string KeySeed, TimeSpan OneWayInterval, TimeSpan ErrorInterval)
		{
			OneWayWorker = new BackgroundWorker();
			OneWayWorker.DoWork += new DoWorkEventHandler(OneWayWorker_DoWork);
			OneWayWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OneWayWorker_RunWorkerCompleted);

			InteractiveWorker = new BackgroundWorker();
			InteractiveWorker.DoWork += new DoWorkEventHandler(InteractiveWorker_DoWork);
			InteractiveWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(InteractiveWorker_RunWorkerCompleted);

			OneWayClient = new Co9320.NamaAlert.Model.TagServer.TagServerClient();
			InteractiveClient = new Co9320.NamaAlert.Model.TagServer.TagServerClient();

			OneWayTransactor = new OneWayTransactor();
			InteractiveTransactor = new InteractiveTransactor();

			OneWayTimer = new DispatcherTimer();
			OneWayTimer.Tick += new EventHandler(OneWayTimer_Tick);
			OneWayTimer.Interval = OneWayInterval;

			InteractiveTimer = new DispatcherTimer();
			InteractiveTimer.Tick += new EventHandler(InteractiveTimer_Tick);
			InteractiveTimer.Interval = OneWayInterval;

			IsOneWayNormalComplete = true;
			IsInteractiveNormalComplete = true;

			TokenGenerator = new TokenProcessor(ApplicationId, KeySeed);
			QueryResult = null;

		}

		void InteractiveTimer_Tick(object sender, EventArgs e)
		{
			InteractiveTimer.Stop();
			InteractiveWorker.RunWorkerAsync();
		}

		private void RaiseEvent()
		{
			foreach (var elem in QueryResult.TagInformations)
			{
				var lst = elem.TagValues.Select<string, Model.TagModel>
                    (tmp => new Model.TagModel(tmp, Co9320.NamaAlert.Model.TagKind.User)).ToList<Model.TagModel>();

				OnTagChanged(this, new TagsChangedEventArgs(elem.StationId, elem.Caster, lst));
			}
		}

		private void OnTagChanged(object sender, TagsChangedEventArgs e)
		{
			if (TagChanged != null)
			{
				TagChanged(sender, e);
			}
		}

		void OneWayTimer_Tick(object sender, EventArgs e)
		{
			OneWayTimer.Stop();
			OneWayWorker.RunWorkerAsync();
		}

		void InteractiveWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (IsInteractiveNormalComplete)
			{
				if (QueryResult != null)
				{
					RaiseEvent();
#if DEBUG
					InteractiveTimer.Interval=new TimeSpan(0,0,5);
#else
					InteractiveTimer.Interval = new TimeSpan(QueryResult.Delay*1000000);
					this.OneWayInterval = this.InteractiveTimer.Interval;
#endif
				}
			}
			else
			{
				InteractiveTimer.Interval = ErrorInterval;
			}

			InteractiveTimer.Start();
		}

		void InteractiveWorker_DoWork(object sender, DoWorkEventArgs e)
		{
			var Entry = InteractiveTransactor.GetEntry();

			try
			{
				InteractiveClient.Open();

				if (Entry != null)
				{
					AuthorizationTokenParameter p = new AuthorizationTokenParameter();
					var Result = InteractiveClient.GetTags(this.GetToken(), Entry);
					this.QueryResult = Result;
				}
				else
				{
					this.QueryResult = null;
				}
			}
			catch (System.ServiceModel.CommunicationException)
			{
				IsInteractiveNormalComplete = false;
			}
			catch (TimeoutException)
			{
				IsInteractiveNormalComplete = false;
			}
			finally
			{
				InteractiveClient.Close();
				InteractiveClient = new TagServerClient();
			}

			IsInteractiveNormalComplete = true;
		}

		void OneWayWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (IsOneWayNormalComplete)
			{
				OneWayTimer.Interval = OneWayInterval;
			}
			else
			{
				OneWayTimer.Interval = ErrorInterval;
			}

			OneWayTimer.Start();
		}

		void OneWayWorker_DoWork(object sender, DoWorkEventArgs e)
		{
			var Params = OneWayTransactor.GetParameters();
			var AddParam = Params.GetAddParameters();
			var DelParam = Params.GetDeleteParameters();
			var ModParam = Params.GetModifyParameters();

			try
			{
				OneWayClient.Open();

				if (AddParam != null)
				{
					OneWayClient.Add(this.GetToken(), AddParam);
				}

				if (DelParam != null)
				{
					OneWayClient.Delete(this.GetToken(), DelParam);
				}

				if (ModParam != null)
				{
					OneWayClient.Modify(this.GetToken(), ModParam);
				}
			}
			catch (System.ServiceModel.CommunicationException)
			{
				IsOneWayNormalComplete = false;
			}
			catch (System.TimeoutException)
			{
				IsOneWayNormalComplete = false;
			}
			finally
			{
				OneWayClient.Close();
				OneWayClient = new TagServerClient();
			}

			IsOneWayNormalComplete = true;
		}

		public void Start()
		{
#if DEBUG
			OneWayTimer.Interval = new TimeSpan(0,0,5);
			InteractiveTimer.Interval = new TimeSpan(0,0,5);
#endif
			OneWayTimer.Start();
			InteractiveTimer.Start();
		}

#if DEBUG
		public void Stop()
		{
			OneWayTimer.Stop();
			InteractiveTimer.Stop();
		}
#endif
		private AuthorizationTokenParameter GetToken()
		{
			AuthorizationTokenParameter param = new AuthorizationTokenParameter();
			param.Time = TokenProcessor.GetTime();
			param.AppricationId = TokenGenerator.ApplicationId;
			param.HashValue = TokenGenerator.GetToken(param.Time);

			return param;
		}


		public void Add(string StationId, string Caster, IList<Model.TagModel> Tags)
		{
			OneWayTransactor.Add(StationId, Caster, Tags);
		}

		public void Delete(string StationId, string Caster, IList<Model.TagModel> Tags)
		{
			OneWayTransactor.Delete(StationId, Caster, Tags);
		}

		public void Get(string StationId,string Caster)
		{
			InteractiveTransactor.AddEntry(StationId, Caster);
		}



		public TimeSpan OneWayInterval
		{
			get;
			private set;
		}


		public TimeSpan ErrorInterval
		{
			get;
			private set;
		}



		#region IDisposable メンバ

		public void Dispose()
		{
			OneWayTimer.Stop();
			InteractiveTimer.Stop();

			OneWayWorker.Dispose();
			InteractiveWorker.Dispose();

			OneWayClient.Abort();
			InteractiveClient.Abort();
		}

		#endregion
	}
}
